#include <bits/stdc++.h>
using namespace std;
struct node{
    int lb, rb, data, data1;
}tree[400001];
struct c{
    int x, y;
}camera[100001], car[100001];
int n, m, L, V, d[100001], u[100001], a[100001], dir[100001], parent[100001], sum[100001], to[100001], ans, ans1, len;
int len1;
int bound(int value){
    int l=0, r=m, mid;
    while(l<r){
        mid=(l+r+1)>>1;
        if(dir[mid]<value){
            l=mid;
        }else{r=mid-1;}
    }
    return l;
}
bool cmp(c i, c j){
    return i.y<j.y;
}
bool cmp1(c i, c j){
    return i.x<j.x;
}
void solve(){
    scanf("%d%d%d%d", &n, &m, &L, &V);
    for(int i=1; i<=n; i++){
        scanf("%d%d%d", &d[i], &u[i], &a[i]);
    }
    for(int i=1; i<=m; i++){
        scanf("%d", &dir[i]);
    }
    sort(dir+1, dir+m+1);
    int xx, yy; ans=0; len1=0; build();
    for(int i=1; i<=n; i++){
        if(a[i]==0){
            if(u[i]>V&&(d[i]<=dir[m])){
                xx=bound(d[i])+1; yy=m;
                if(xx<=yy){
                    ans++; add(1, xx, yy);
                }
            }else{
                continue;
            }
        }else if(a[i]<0){
            if(u[i]<=V){continue;}
            xx=bound(d[i])+1; yy=bound(d[i]+(u[i]*u[i]-V*V)/(-2*a[i]));
            if(xx<=yy){
                ans++; add(1, xx, yy);
            }
        }else if(u[i]<=V){
            xx=bound(d[i]+(V*V-u[i]*u[i])/(2*a[i])+1)+1; yy=m;
            if(xx<=yy){
                ans++; add(1, xx, yy);
            }
        }else{
            xx=bound(d[i])+1; yy=m;
            if(xx<=yy){
                ans++; add(1, xx, yy);
            }
        }
        if(xx<=yy){
            car[++len1].x=xx;
            car[len1].y=yy;
        }
    }
    ans1=0; len=0; spread();
    sort(camera+1, camera+len+1, cmp);
    sort(car+1, car+len1+1, cmp1);
    for(int i=1; i<=len1; i++){
        int res=query(1, car[i].x, car[i].y)
        parent[res]++; to[i]=res;
    }
    for(int i=1, j=1; i<=len; i++){
        if(!parent[camera[i].x]){
            ans1++;
        }else{
            while((car[j].x==camera[i].x)&&(j<=len1)){
                parent[j]=camera[i].x; j++;
            }
        }
        while((car[j].x==camera[i].x)){
            parent[j]=camera[i].x; j++;
        }
    }
    printf("%d %d\n", ans, ans1);
    for(int i=1; i<=m; i++){
        dir[i]=0;
    }
}
void build(int x, int l, int r){
    if(l==r){tree[x].lb=l; tree[x].rb=r; tree[x].data=0; tree[x].data1=x; return;}
    int mid=(l+r)>>1;
    build(x*2, l, mid); build(x*2+1, mid, r);
}
void add(int x, int l, int r){
    if(tree[x].lb>r||tree[x].rb<l){return;}
    if((tree[x].lb>=l)&&(tree[x].rb<==r)){
        tree[x].data+=1; return;
    }else{
        add(x*2, l, r); add(x*2+1, l, r);
    }
}
int query(int x, int l, int r){
    if(tree[x].lb>r||tree[x].rb<l){return 0;}
    if((tree[x].lb>=l)&&(tree[x].rb<=r)){
        return tree[x].data1;
    }else{
        int xx1=query(x*2, l, r), yy1=query(x*2+1, l, r);
        return ((tree[xx1].data>tree[yy1].data)?xx1:yy1);
    }
}
void spread(int x, int v){
    tree[x].data+=v;
    if(tree[x].lb==tree[x].rb){
        if(!tree[x].data){
            ans1++;
        }else{
            camera[++len].x=tree[x].lb;
            camera[++len].y=tree[x].data;
        }
    }else{
        spread(x*2, tree[x].data);
        spread(x*2+1, tree[x].data);
        tree[x].data1=((tree[x*2].data>tree[x*2+1].data)?tree[x*2].data1:tree[x*2+1].data1);
        tree[x].data=max(tree[x*2].data, tree[x*2+1].data);
    }
}
int main(){
    freopen("detect.in", "r", stdin);
    freopen("detect.out", "w", stdout);
    int T; scanf("%d", &T);
    while(T--){
        solve();
    }
    return 0;
}
